{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Семинар 3. Классы. Введение"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Мой первый класс"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Реализуем класс - строку на С++.\n",
    "\n",
    "Важнейший принцип языка, основа его основ - RAII (Resource Acquisition Is Initialization)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "class String\n",
    "{\n",
    "public:\n",
    "    String();\n",
    "    String(const char *s);\n",
    "    String(const char *s, int size);\n",
    "    ~String();\n",
    "    String(const String& rhs);\n",
    "    String(String&& rhs);\n",
    "\n",
    "    String& operator = (const String& rhs);\n",
    "    String& operator = (String&& rhs);\n",
    "\n",
    "    friend String operator + (const String& lhs, const String& rhs);\n",
    "\n",
    "private:\n",
    "    char* s_;  // pointer to null-terminated characters\n",
    "    size_t l_; // strlen(s_) == l_\n",
    "};\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Для начала реализуем оператор сложения двух строк"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "String operator + (const String& lhs, const String& rhs)\n",
    "{\n",
    "    const size_t res_size = lhs.l_ + rhs.l_;\n",
    "    const char* res_s = new char[res_size + 1];\n",
    "    strcpy(res_s, lhs.s_);\n",
    "    strncpy(res_s + lhs.l_, rhs.s_, rhs.l_);\n",
    "    \n",
    "    String s(res_s, res_size);\n",
    "\n",
    "    delete[] res_s;\n",
    "    return s;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Реализуем конструкторы"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "String::String()\n",
    "    : s_(new char[1])\n",
    "    , l_(0)\n",
    "{\n",
    "    s_[0] = 0;\n",
    "}\n",
    "\n",
    "String::String(const char* s)\n",
    "{\n",
    "    l_ = strlen(s);\n",
    "    s_ = new char[l + 1];\n",
    "    strcpy(s_, s);\n",
    "}\n",
    "\n",
    "String::String(const char *s, int size)\n",
    "    : s_(new char[size + 1])\n",
    "    , l_(size + 1)\n",
    "{\n",
    "    strncpy(s_, s, size);\n",
    "    s_[l_] = 0;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Реализуем деструктор - код, который будет вызываться при уничтожении объекта"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "String::~String()\n",
    "{\n",
    "    delete[] s_;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Остановимся на секунду и всомним про RAII"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Реализуем конструктор копирования"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "String::String(const String& rhs)\n",
    "{\n",
    "    s_ = new char[rhs.l_ + 1];\n",
    "    l_ = rhs.l_;\n",
    "    strcpy(s_, rhs.s_);\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "А теперь конструктор перемещения"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "String::String(String&& rhs)\n",
    "    : s_(rhs.s_)\n",
    "    , l_(rhs.l_)\n",
    "{\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Что-то пошло не так. Что именно?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Не работает такой код, а должен:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "{\n",
    "    String s1 = \"run, Forest, run!\";\n",
    "    String s2 = std::move(s1);\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Реализуем правильный конструктор перемещения"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "String::String(String&& rhs)\n",
    "    : s_(rhs.s_)\n",
    "    , l_(rhs.l_)\n",
    "{\n",
    "    rhs.s_ = 0;\n",
    "    rhs.l_ = 0;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "А точно ли он правильный?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Спойлер: rhs сломан"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Третья попытка реализовать правильный конструктор перемещения"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "String::String(String&& rhs)\n",
    "{\n",
    "    s_ = rhs.s_;\n",
    "    l_ = rhs.l_;\n",
    "\n",
    "    rhs.s_ = new char[1];\n",
    "    rhs.s_[0] = 0;\n",
    "    rhs.l_ = 0;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Теперь реализуем оператор копирующего присваивания:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "String& String::operator =(const String& rhs)\n",
    "{\n",
    "    delete[] s_;\n",
    "\n",
    "    s_ = new char[rhs.l_ + 1];\n",
    "    l_ = rhs.l_;\n",
    "    strcpy(s_, rhs.s_);\n",
    "\n",
    "    return *this;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Где ошибка?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Правильная реализация будет выглядеть так:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "String& String::operator =(const String& rhs)\n",
    "{\n",
    "    if (this != &rhs)\n",
    "    {\n",
    "        delete[] s_;\n",
    "\n",
    "        s_ = new char[rhs.l_ + 1];\n",
    "        l_ = rhs.l_;\n",
    "        strcpy(s_, rhs.s_);\n",
    "    }\n",
    "    return *this;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Или нет?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "И оператор перемещающего присваивания:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```c++\n",
    "String& String::operator =(String&& rhs)\n",
    "{\n",
    "    if (this != &rhs)\n",
    "    {\n",
    "        delete[] s_;\n",
    "\n",
    "        s_ = rhs.s_;\n",
    "        l_ = rhs.l_;\n",
    "\n",
    "        rhs.s_ = new char[1];\n",
    "        rhs.s_[0] = 0;\n",
    "        rhs.l_ = 0;\n",
    "    }\n",
    "\n",
    "    return *this;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Замечания:\n",
    "* Методы перемещения && плохи, они кидают исключения, лучше реализовать класс таким образом, чтобы методы перемещения не кидали исключений и объявить их noexcept. Как это сделать?\n",
    "* Деструктор должен быть noexcept"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<br />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Порядок конструирования"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# %load 1_ctors_order.cpp\n",
    "#include <iostream>\n",
    "\n",
    "\n",
    "class Name\n",
    "{\n",
    "public:\n",
    "\tName() { std::cout << \"Name \"; }\n",
    "\t~Name() { std::cout << \"~Name \"; }\n",
    "};\n",
    "\n",
    "class Leg\n",
    "{\n",
    "public:\n",
    "\tLeg() { std::cout << \"Leg \"; }\n",
    "\t~Leg() { std::cout << \"~Leg \"; }\n",
    "};\n",
    "\n",
    "class Tail\n",
    "{\n",
    "public:\n",
    "\tTail() { std::cout << \"Tail \"; }\n",
    "\t~Tail() { std::cout << \"~Tail \"; }\n",
    "};\n",
    "\n",
    "class Animal\n",
    "{\n",
    "public:\n",
    "\tAnimal() { std::cout << \"Animal \"; }\n",
    "\t~Animal() { std::cout << \"~Animal \"; }\n",
    "\n",
    "private:\n",
    "\tName name_;\n",
    "};\n",
    "\n",
    "class Cat : public Animal\n",
    "{\n",
    "public:\n",
    "\tCat() { std::cout << \"Cat \"; }\n",
    "\t~Cat() { std::cout << \"~Cat \"; }\n",
    "\n",
    "private:\n",
    "\tLeg l1_;\n",
    "\tLeg l2_;\n",
    "\tLeg l3_;\n",
    "\tLeg l4_;\n",
    "\tTail tail_;\n",
    "};\n",
    "\n",
    "\n",
    "// show this later\n",
    "// Animal animal;\n",
    "\n",
    "int main()\n",
    "{\n",
    "\tstd::cout << \"hello!\" << std::endl;\n",
    "\tCat cat;\n",
    "\tstd::cout << std::endl << \"goodbye!\" << std::endl;\n",
    "\treturn 0;\n",
    "}\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Порядок вызова конструкторов:\n",
    "1. сначала базовый класс\n",
    "2. потом члены в порядке их объявления\n",
    "3. потом конструктор самого класса"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Порядок вызова деструкторов:\n",
    "* обратно порядку вызова конструкторов"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Скомпилируем и запустим"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "hello!\r\n",
      "Name Animal Leg Leg Leg Leg Tail Cat \r\n",
      "goodbye!\r\n",
      "~Cat ~Tail ~Leg ~Leg ~Leg ~Leg ~Animal ~Name "
     ]
    }
   ],
   "source": [
    "!clang++ -O3 1_ctors_order.cpp && ./a.out && rm -f a.out"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
